﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once
#include "Vector2d.h"

namespace gcmp
{

    /// \brief   三维向量的定义
    ///
    /// 
    class GCMP_MATH_INTERFACE_EXPORT Vector3d
    {
    public:
        DEFINE_VISUAL_TYPE_DOUBLE()

    public:

        /// \brief    创建三维向量
        ///
        /// \param x  X分量
        /// \param y  Y分量
        /// \param z  Z分量
        /// \return  三维向量
        static  Vector3d Create(const double x, const double y, const double z);

        /// \brief   创建三维零向量
        ///
        /// \return  三维零向量
        static Vector3d CreateZeroVector3d();

        /// \brief   获取三维向量X分量
        ///
        /// \return  X分量
        double GetX() const;
        
        /// \brief   获取三维向量Y分量
        ///
        /// \return  Y分量
        double GetY() const;
        
        /// \brief   获取三维向量Z分量
        ///
        /// \return  Z分量
        double GetZ() const;

        /// \brief    设置该向量的X、Y分量以及Z分量
        ///
        /// \param x  X分量
        /// \param y  Y分量
        /// \param z  Z分量
        /// \return  true:成功， false:失败
        bool Set(const double x, const double y, const double z);

        /// \brief   设置该向量的X分量
        ///
        /// \param x  X分量
        /// \return  true:成功， false:失败
        bool SetX(const double x);

        /// \brief   设置该向量的Y分量
        ///
        /// \param y  Y分量
        /// \return   true:成功， false:失败
        bool SetY(const double y);

        /// \brief    设置该向量的Z分量
        ///
        /// \param z  Z分量
        /// \return   true:成功， false:失败
        bool SetZ(const double z);

        /// \brief     判断向量在给定误差下是否零向量
        ///
        /// \param epsilon  误差
        /// \return   true:是， false:否
        bool IsZero(const double epsilon = Constants::DOUBLE_EPS) const;

        /// \brief   判断向量在给定误差下是否单位向量
        ///
        /// \param epsilon 误差
        /// \return  true:是， false:否
        bool IsUnit(const double epsilon = Constants::DOUBLE_EPS) const;

        /// \brief    向量与向量相减
        ///
        /// \param vecSrc  给定向量
        /// \return  向量的差
        Vector3d Substract(const Vector3d & vecSrc) const;

        /// \brief    计算向量与给定向量的点积
        ///
        /// \param vecSrc  给定向量
        /// \return   点积
        double Dot(const Vector3d & vecSrc) const;

        /// \brief   计算向量与给定向量的叉积
        ///
        /// \param vecSrc  给定向量
        /// \return  叉积
        Vector3d Cross(const Vector3d & vecSrc) const;

        /// \brief    计算向量的长度
        ///
        /// \return   向量的长度
        double Length() const;

        /// \brief   计算向量长度的平方
        ///
        /// \return  向量长度的平方
        double SqrLength() const;

        /// \brief  向量的单位化
        ///
        /// 注意：本函数是纯粹的单位化操作，既没有对 Vector3d 的有效性进行判断，判 0 时也没有考虑容差，
        /// 从代码健壮性考虑，建议在调用本函数前先调用以下两个函数以避免对非法 Vector3d 进行单位化操作：
        /// 1、IsValid
        /// 2、IsZero
        /// \return 单位化前向量的长度。0 表示 Vector3d 长度为 0（不带容差），无法归一化。
        double Normalize();

    public:

        /// \brief   默认构造函数
        /// 
        Vector3d();

        /// \brief   通过长度为3的数组构造三维向量
        ///
        /// \param v 长度为3的数组
        /// 
        explicit Vector3d(const double v[3]);

        /// \brief    通过向量的x,y分量和z分量构造三维向量
        ///
        /// \param x  x分量
        /// \param y  y分量
        /// \param z  z分量
        /// 
        Vector3d(const double x, const double y, const double z);

        /// \brief    根据二维点和给定z值组合三维点
        ///
        /// \param vecSrc2d  二维坐标点
        /// \param z  z分量
        /// 
        Vector3d(const Vector2d &vecSrc2d, const double z);

        /// \brief   拷贝构造函数
        ///
        /// \param vecSrc 三维坐标点
        /// 
        Vector3d(const Vector3d& vecSrc);

        /// \brief  获取X分量
        ///
        /// \return  X分量
        double X() const;

        /// \brief  获取Y分量
        ///
        /// \return  Y分量
        double Y() const;

        /// \brief  获取Z分量
        ///
        /// \return  Z分量
        double Z() const;

        /// \brief  获取X分量的引用
        ///
        /// \return  X分量的引用
        double& X();

        /// \brief  获取Y分量的引用
        ///
        /// \return  Y分量的引用
        double& Y();

        /// \brief  获取Z分量的引用
        ///
        /// \return  Z分量的引用
        double& Z();

        /// \brief   前两个分量的向量
        ///
        /// \return  结果向量
        Vector2d  Vec2d() const;

        /// \brief  坐标是否是有效的坐标值
        ///
        /// \return  true:是， false:否
        bool IsValid() const;

        /// \brief   通过给定数据设置向量
        ///
        /// \param v 给定数组
        /// \return true:成功， false:失败
        bool Set(const double v[3]);

        /// \brief   根据二维点和给定z值组合三维点
        ///
        /// \param vecSrc2d  二维坐标点
        /// \param z  z分量
        /// \return  true:成功， false:失败
        bool Set(const Vector2d &vecSrc2d, const double z);

        /// \brief   赋值函数
        ///
        /// \param d  三维坐标点
        /// \return  当前坐标点的引用
        Vector3d & operator =(const Vector3d &d);

        /// \brief   按照索引获取向量的分量
        ///
        /// \param nIndex 索引，由调用者确保分量索引的有效性
        /// \return  向量分量值
        double& operator[](int nIndex);

        /// \brief   按照索引获取向量的分量
        ///
        /// \param nIndex 索引，由调用者确保分量索引的有效性
        /// \return  向量分量值
        const double & operator[](int nIndex) const;

        /// \brief   两个向量的加法，即向量的每个分量相加
        ///
        /// \param  v   另一个向量
        /// \return  向量的和
        Vector3d operator +(const Vector3d & v) const;

        /// \brief   两个向量的减法，即向量1的每个分量减去向量2的每个分量
        ///
        /// \param  v1   向量1
        /// \param  v2   向量2
        /// \return  向量的差
        Vector3d operator -(const Vector3d & v) const;

        /// \brief   计算向量与给定向量的点积
        ///
        /// \param  v 给定向量
        /// \return  点积
        double operator * (const Vector3d &v) const;

        /// \brief   该向量乘给定数值
        ///
        /// \param  d 乘数
        /// \return  结果向量
        Vector3d & operator *=(const double &d);

        /// \brief   该向量除以给定数值
        ///
        /// \param  d 除数，调用者确保输入参数的有效性，不能为0
        /// \return  结果向量
        Vector3d & operator /=(const double &d);

        /// \brief   向量与给定向量求和
        ///
        /// \param  vecSrc 给定向量
        /// \return  和向量引用
        Vector3d & operator +=(const Vector3d & vecSrc);

        /// \brief   向量与给定向量求差
        ///
        /// \param  vecSrc 给定向量
        /// \return  差向量引用
        Vector3d & operator -=(const Vector3d & vecSrc);

        /// \brief   向量反向
        ///
        /// \return  反向量
        Vector3d operator-() const;

        /// \brief   包围盒对象按照规定格式字符串输出
        ///
        /// \return  字符串
        std::wstring GetStringForDebug() const;

    private:
        double m_xyz[3];

    public:
        static const Vector3d UnitX;
        static const Vector3d UnitY;
        static const Vector3d UnitZ;
        static const Vector3d NegaUnitX;
        static const Vector3d NegaUnitY;
        static const Vector3d NegaUnitZ;
        static const Vector3d Zero;
    };

    /// \brief   计算向量乘给定数值
    ///
    /// \param  vecSrc 给定向量
    /// \param  d 乘数
    /// \return  结果向量
    GCMP_MATH_INTERFACE_EXPORT Vector3d operator *(const Vector3d & vecSrc, const double d);

    /// \brief   计算数值乘给定向量
    ///
    /// \param  d 乘数
    /// \param  vecSrc 给定向量
    /// \return  结果向量
    GCMP_MATH_INTERFACE_EXPORT Vector3d operator *(const double &d, const Vector3d & vecSrc);

    /// \brief   两个向量的叉乘
    ///
    /// \param  v1   向量1
    /// \param  v2   向量2
    /// \return  叉乘结果
    GCMP_MATH_INTERFACE_EXPORT Vector3d operator^(const Vector3d & v1, const Vector3d & v2);

    /// \brief   向量除以一个数
    ///
    /// \param  vecSrc  被除向量
    /// \param  d       除数
    /// \return  结果向量
    GCMP_MATH_INTERFACE_EXPORT Vector3d operator /(const Vector3d & vecSrc, double d);

    /// \brief   判断向量v1是否大于向量v2
    ///
    /// \param  v1  向量v1
    /// \param  v2  向量v2
    /// \return  true: 是， false:否
    GCMP_MATH_INTERFACE_EXPORT bool operator > (const Vector3d & v1, const Vector3d & v2);

    /// \brief   判断向量v1是否小于向量v2
    ///
    /// \param  v1  向量v1
    /// \param  v2  向量v2
    /// \return  true: 是， false:否
    GCMP_MATH_INTERFACE_EXPORT bool operator < (const Vector3d & v1, const Vector3d & v2);

    /// \brief   判断向量是否相同
    ///
    /// \param  v1  向量v1
    /// \param  v2  向量v2
    /// \return  true: 相同   false: 不同
    GCMP_MATH_INTERFACE_EXPORT bool operator ==(const Vector3d & v1, const Vector3d & v2);

    /// \brief   判断向量是否不同
    ///
    /// \param  v1  向量v1
    /// \param  v2  向量v2
    /// \return  true: 不同   false: 相同
    GCMP_MATH_INTERFACE_EXPORT bool operator !=(const Vector3d & v1, const Vector3d & v2);

} // namespace


